﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.SharePoint;
using Microsoft.VisualStudio.TextTemplating;
using SharePoint.Orm.Helpers;
using SharePoint.Orm.Metal.Model;
using SharePoint.Orm.Metal.T4;

namespace SharePoint.Orm.Metal {
	public class CodeGenerator {
		public void Generate(CodeGeneratorConfiguration config) {
			using (SPSite web = new SPSite(config.Url))
			using (SPWeb site = web.OpenWeb()) {
				SiteInfo siteInfo = BuildSiteContent(config, site);
				GenerateDataContext(config, siteInfo);
				foreach (ListInfo list in siteInfo.Lists) {
					GenerateList(config, list);
				}
				foreach (ContentTypeInfo contentType in siteInfo.ContentTypes) {
					GenerateContentType(config, contentType);
				}
			}
		}

		private void GenerateList(CodeGeneratorConfiguration config, ListInfo listInfo) {
			using (Stream templateStream = ResourceHelper.OpenStream(config.ListTemplate)) {
				string output = Generate(templateStream, listInfo);
				Directory.CreateDirectory(config.ListOutputDirectory);
				string fileName = Path.Combine(config.ListOutputDirectory, listInfo.FileName);
				File.WriteAllText(fileName, output);
			}
		}

		private void GenerateContentType(CodeGeneratorConfiguration config, ContentTypeInfo contentTypeInfo) {
			using (Stream templateStream = ResourceHelper.OpenStream(config.ContentTypeTemplate)) {
				string output = Generate(templateStream, contentTypeInfo);
				Directory.CreateDirectory(config.ContentTypeOutputDirectory);
				string fileName = Path.Combine(config.ContentTypeOutputDirectory, contentTypeInfo.FileName);
				File.WriteAllText(fileName, output);
			}
		}

		private void GenerateDataContext(CodeGeneratorConfiguration config, SiteInfo siteInfo) {
			using (Stream templateStream = ResourceHelper.OpenStream(config.DataContextTemplate)) {
				string output = Generate(templateStream, siteInfo);
				Directory.CreateDirectory(config.DataContextOutputDirectory);
				string fileName = Path.Combine(config.DataContextOutputDirectory, siteInfo.DataContextFileName);
				File.WriteAllText(fileName, output);
			}
		}

		private static string Generate(Stream templateStream, object data) {
			TemplateHost host = new TemplateHost {
				TemplateFile = "SharePointOrm.tt"
			};
			CustomDirectiveProcessor.Parameters = data;

			string template = new StreamReader(templateStream).ReadToEnd();
			ITextTemplatingEngine engine = new Engine();
			string output = engine.ProcessTemplate(template, host);
			return output;
		}

		private static SiteInfo BuildSiteContent(CodeGeneratorConfiguration config, SPWeb site) {
			SiteInfo siteInfo = new SiteInfo(config);

			foreach (SPList list in site.Lists) {
				if (config.IncludeList(list)) {
					foreach (SPContentType contentType in list.ContentTypes) {
						siteInfo.AddContentType(contentType);
					}
					siteInfo.AddList(list);
				}
			}

			// resolve name conflicts
			IEnumerable<ContentTypeInfo> conflicts = siteInfo.ContentTypes
				.Select(ct => siteInfo.ContentTypes.Where(c => string.Equals(c.ContentTypeName, ct.ContentTypeName, StringComparison.InvariantCultureIgnoreCase)))
				.Where(c => c.Count() > 1)
				.SelectMany(c => c)
				.Distinct();
			foreach (ContentTypeInfo conflict in conflicts) {
				conflict.ClassName =
					config.NameRewriter.ToContentTypeName(conflict.ContentType, conflict.ContentType.ParentList.Title + conflict.ClassName);
			}

			return siteInfo;
		}
	}
}
